package org.abettor.angleworm.media.conf;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

import javax.media.CaptureDeviceInfo;
import javax.media.CaptureDeviceManager;
import javax.media.format.AudioFormat;
import javax.media.format.VideoFormat;

/**
 * 设备配置信息
 * @author abettor
 *
 */
public class DeviceConfiguration
{
	private static byte[] locker = new byte[0];
	private static DeviceConfiguration conf = null;
	private File confFile;
	private Properties prop = new Properties();
	private boolean writeBack = false;
	private InetAddress localAddr;
	private CaptureDeviceInfo audioCapture;
	private CaptureDeviceInfo videoCapture;

	/**
	 * 获取设备配置信息对象
	 * @throws IOException 
	 */
	public static DeviceConfiguration getInstance() throws IOException
	{
		synchronized(locker)
		{
			if(conf == null)
			{
				conf = new DeviceConfiguration();
				conf.configureNetwork();
				conf.configureAudioCapture();
				conf.configureVideoCapture();
				conf.updateConfiguration();
			}
		}

		return conf;
	}

	//	配置网络信息
	private void configureNetwork() throws IOException
	{
		String bindAddr = prop.getProperty("BIND_ADDRESS");
		if(bindAddr != null && ! bindAddr.isEmpty())
		{
			localAddr = InetAddress.getByName(bindAddr);
			return;
		}

		System.out.println("\n[ Bindable Local Address List ]");
		List<InetAddress> addrList = new ArrayList<InetAddress>();
		Enumeration<NetworkInterface> inters = NetworkInterface.getNetworkInterfaces();
		while(inters.hasMoreElements())
		{
			NetworkInterface inter = inters.nextElement();
			Enumeration<InetAddress> addrs = inter.getInetAddresses();
			while(addrs.hasMoreElements())
			{
				InetAddress addr = addrs.nextElement();
				System.out.println("\t" + addrList.size() + "\t[" + inter.getName() + "] " + addr.getHostAddress() + " {" + addr.getHostName() + "}");
				addrList.add(addr);
			}
		}

		if(addrList.isEmpty())
		{
			System.out.println("\nThere is no bindable local network address. Application will not run properly.");
			return;
		}

		System.out.println("\nPlease input the index of the address to bind:\n\t(The address must be of the same host name with you computer.)");
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String index = br.readLine();
		int idx = Integer.parseInt(index);
		localAddr = addrList.get(idx);

		if(localAddr != null)
		{
			prop.setProperty("BIND_ADDRESS", localAddr.getHostAddress());
			writeBack = true;
		}
	}

	//	配置音频设备信息
	@SuppressWarnings("unchecked")
	private void configureAudioCapture() throws IOException
	{
		String audioCapName = prop.getProperty("AUDIO_DEVICE");
		if(audioCapName != null && ! audioCapName.isEmpty())
		{
			audioCapture = CaptureDeviceManager.getDevice(audioCapName);
			return;
		}

		System.out.println("\n[ Audio Capture Devices List ]");
		int n = 0;
		AudioFormat af = new AudioFormat(AudioFormat.LINEAR);
		Vector<CaptureDeviceInfo> devices = CaptureDeviceManager.getDeviceList(af);
		for(CaptureDeviceInfo cdi : devices)
		{
			System.out.println("\t" + n + "\t" + cdi.getName());
			n ++;
		}

		if(devices.isEmpty())
		{
			System.out.println("\nThere is no audio capture device. Application will not run properly.");
			return;
		}

		System.out.println("\nPlease input the index of the voice input device you want to use:");
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String index = br.readLine();
		int idx = Integer.parseInt(index);
		audioCapture = devices.get(idx);

		if(audioCapture != null)
		{
			prop.setProperty("AUDIO_DEVICE", audioCapture.getName());
			writeBack = true;
		}
	}

	//	配置视频设备信息
	@SuppressWarnings("unchecked")
	private void configureVideoCapture() throws IOException
	{
		String videoCapName = prop.getProperty("VIDEO_DEVICE");
		if(videoCapName != null && ! videoCapName.isEmpty())
		{
			videoCapture = CaptureDeviceManager.getDevice(videoCapName);
			return;
		}

		System.out.println("\n[ Video Capture Devices List ]");
		int n = 0;
		VideoFormat vf = new VideoFormat(VideoFormat.YUV);
		Vector<CaptureDeviceInfo> devices = CaptureDeviceManager.getDeviceList(vf);
		for(CaptureDeviceInfo cdi : devices)
		{
			System.out.println("\t" + n + "\t" + cdi.getName());
			n ++;
		}

		if(devices.isEmpty())
		{
			System.out.println("\nThere is no video capture device. Application will not run properly.");
			return;
		}

		System.out.println("\nPlease input the index of the video input device you want to use:");
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String index = br.readLine();
		int idx = Integer.parseInt(index);
		videoCapture = devices.get(idx);

		if(videoCapture != null)
		{
			prop.setProperty("VIDEO_DEVICE", videoCapture.getName());
			writeBack = true;
		}
	}

	//	私有构造函数，禁止直接实例化
	private DeviceConfiguration() throws IOException
	{
		//	获取当前应用程序的安装目录
		File f = new File(".");
		String appPath = f.getAbsolutePath();
		if(appPath.endsWith(File.separator + "."))
		{
			appPath = appPath.substring(0, appPath.length() - 2);
		}
		if(appPath.endsWith(File.separator + "bin"))
		{
			appPath = appPath.substring(0, appPath.length() - 4);
		}

		//	获得配置文件
		String confPath = appPath + File.separator + "conf" + File.separator + "device.properties";
		confFile = new File(confPath);
		if(confFile.exists())
		{
			//	读取配置信息
			FileInputStream fis = new FileInputStream(confFile);
			prop.load(fis);
			fis.close();
			return;
		}

		//	不存在文件则创建一个新文件
		confFile.createNewFile();
		writeBack = true;
	}

	//	更新配置信息
	@SuppressWarnings("deprecation")
	private void updateConfiguration() throws IOException
	{
		if(! writeBack)
		{
			return;
		}

		FileOutputStream fos = new FileOutputStream(confFile);
		prop.save(fos, null);
		fos.flush();
		fos.close();
	}

	public InetAddress getLocalAddr()
	{
		return localAddr;
	}

	public CaptureDeviceInfo getAudioCapture()
	{
		return audioCapture;
	}

	public CaptureDeviceInfo getVideoCapture()
	{
		return videoCapture;
	}
}
